package com.leyou.user.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.leyou.common.constants.MQConstants;
import com.leyou.common.enums.ExceptionEnum;
import com.leyou.common.exception.LyException;
import com.leyou.common.utils.BeanHelper;
import com.leyou.common.utils.RegexUtils;
import com.leyou.user.dto.UserDTO;
import com.leyou.user.entity.TbUser;
import com.leyou.user.mapper.TbUserMapper;
import com.leyou.user.service.TbUserService;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author HM
 * @since 2019-10-25
 */
@Service
public class TbUserServiceImpl extends ServiceImpl<TbUserMapper, TbUser> implements TbUserService {


    @Autowired
    private AmqpTemplate amqpTemplate;

    /*操作Redis的Template*/
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /*注入加密的Bean*/
    @Autowired
    private BCryptPasswordEncoder encoder;

    /*Redis的命名规约：ly:工程：user微服务：verify业务：phone：作为key*/
    private  String PRE_FIX="ly:user:verify:phone";
    /**
     *
     * @param data
     * @param type
     * @return
     */
    @Override
    public boolean  check(String data, Integer type) {

        /*查询数据库：构建查询条件*/
        QueryWrapper<TbUser> queryWrapper=new QueryWrapper<>();

        /*先判断数据类型，再去校验数据*/
        switch (type){
            case 1: queryWrapper.lambda().eq(TbUser::getUsername,data);
              break;
            case 2: queryWrapper.lambda().eq(TbUser::getPhone,data);
              break;
            default:
                throw new LyException(ExceptionEnum.INVALID_PARAM_ERROR);
        }
       // TbUser tbUser = this.getOne(queryWrapper); tbUser==null
        int count = this.count(queryWrapper); //count==0为true
        /*如果user不存在，数据可用，如果user存在，数据不可用*/
        return  count==0;
    }


    /*采用消息队列，发送消息，短信服务接受消息后，发送短息*/
    @Override
    public void sendCode(String phone) {
/*
*
*
        将验证码保存在服务端
        发送短信，将验证码发送到用户手机
        那么问题来了：验证码保存在哪里呢？
        采用redis存储数据，可以设置有效的时间，redis的过期机制
        验证码有一定有效期，一般是5分钟，
        我们可以利用Redis的过期机制来保存。
        1.生成验证码
        2.将验证码保存到redis中，并设置有效时间，以便用户注册的时候，比对验证码

* */

        //校验手机号:正则表达式校验
        if(!RegexUtils.isPhone(phone)){
            throw new LyException(ExceptionEnum.INVALID_PHONE_NUMBER);
        }
        String code = RandomStringUtils.randomNumeric(6);
        /*存入Redis:不能直接把手机号作为key存储，因为，后续的业务，可能会有冲突
        * 解决方案：给手机号key加上业务的前缀，用于表明验证码业务
        * Redis的命名规约
        * :参数：key,value,有效时间5分钟，TimeUnit.MINUTES单位分钟
        * */
        stringRedisTemplate.opsForValue().set(PRE_FIX+phone,code,5,TimeUnit.MINUTES);

        JSONObject jsonObject=new JSONObject();

        jsonObject.put("phone",phone);
        /*生成随机验证码，长度6位，纯数字 RandomStringUtils.randomNumeric(6)*/
        jsonObject.put("code",code);
        amqpTemplate.convertAndSend(MQConstants.Exchange.SMS_EXCHANGE_NAME,
                MQConstants.RoutingKey.VERIFY_CODE_KEY,
                jsonObject);
    }


    /*注册用户*/
    @Override
    public void registerUser(TbUser user,String code ) {
        /*
        * 1.校验验证码：Redis中获取验证码
        * 2.密码加密
        * 3.保存数据
        * */
        String codeRedis = stringRedisTemplate.opsForValue().get(PRE_FIX + user.getPhone());

        /*判断code是否存在
          判断code是否一样
         */
        if(StringUtils.isEmpty(codeRedis)||!codeRedis.equals(code)){
            throw new LyException(ExceptionEnum.INVALID_VERIFY_CODE);
        }
        /*密码加密：采用Spring提供的加密*/
        String encodePassword = encoder.encode(user.getPassword());
        user.setPassword(encodePassword);//将加密后的密码，保存至Tb对象中
        /*保存数据*/
        boolean save = this.save(user);
        if(!save){
            throw  new LyException(ExceptionEnum.INSERT_OPERATION_FAIL);
        }
    }


    /**
     * 根据用户名，密码查询用户
     * @param username 用户名
     * @param password 密码
     *
     * 1.根据用户名，查询数据库
     * 2.根据原密码和加密的密码，进行解密
     */
    @Override
    public UserDTO queryUser(String username, String password) {
        QueryWrapper<TbUser> queryWrapper=new QueryWrapper<>();
        queryWrapper.lambda().eq(TbUser::getUsername,username);
        TbUser tbUser= this.getOne(queryWrapper);
        /*判断查询的用户*/
        if(tbUser==null){
            throw new LyException(ExceptionEnum.INVALID_USERNAME_PASSWORD);
        }
        /*密码判断*/
        boolean matches = encoder.matches(password, tbUser.getPassword());
        if(!matches){
            throw new LyException(ExceptionEnum.INVALID_USERNAME_PASSWORD);
        }
        /*对象转化*/
        UserDTO userDTO = BeanHelper.copyProperties(tbUser, UserDTO.class);
        return userDTO;
    }
}
